Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | /** * API route for worksheet parsing status and cancellation * * GET /api/curriculum/[playerId]/attachments/[attachmentId]/parse * - Get current parsing status and results * * DELETE /api/curriculum/[playerId]/attachments/[attachmentId]/parse * - Cancel/reset parsing status * * NOTE: POST (start parsing) has been removed. Parsing is now initiated via * the task-based route at /parse/task which uses the background task system * with Socket.IO for real-time streaming updates. */ import { NextResponse } from 'next/server' import { eq } from 'drizzle-orm' import { db } from '@/db' import { practiceAttachments, type ParsingStatus } from '@/db/schema/practice-attachments' import { withAuth } from '@/lib/auth/withAuth' import { canPerformAction } from '@/lib/classroom' import { getUserId } from '@/lib/viewer' import { computeParsingStats, type WorksheetParsingResult } from '@/lib/worksheet-parsing' /** * GET - Get parsing status and results */ export const GET = withAuth(async (_request, { params }) => { try { const { playerId, attachmentId } = (await params) as { playerId: string; attachmentId: string } if (!playerId || !attachmentId) { return NextResponse.json({ error: 'Player ID and Attachment ID required' }, { status: 400 }) } // Authorization check const userId = await getUserId() const canView = await canPerformAction(userId, playerId, 'view') if (!canView) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } // Get attachment record const attachment = await db .select() .from(practiceAttachments) .where(eq(practiceAttachments.id, attachmentId)) .get() if (!attachment) { return NextResponse.json({ error: 'Attachment not found' }, { status: 404 }) } if (attachment.playerId !== playerId) { return NextResponse.json({ error: 'Attachment not found' }, { status: 404 }) } // Build response based on status const response: { status: ParsingStatus | null parsedAt: string | null result: WorksheetParsingResult | null error: string | null needsReview: boolean confidenceScore: number | null stats?: ReturnType<typeof computeParsingStats> llm?: { provider: string | null model: string | null promptUsed: string | null rawResponse: string | null jsonSchema: string | null imageSource: string | null attempts: number | null usage: { promptTokens: number | null completionTokens: number | null totalTokens: number | null } } } = { status: attachment.parsingStatus, parsedAt: attachment.parsedAt, result: attachment.rawParsingResult, error: attachment.parsingError, needsReview: attachment.needsReview === true, confidenceScore: attachment.confidenceScore, } // Add stats if we have results if (attachment.rawParsingResult) { response.stats = computeParsingStats(attachment.rawParsingResult) } // Add LLM metadata if available if (attachment.llmProvider || attachment.llmModel) { response.llm = { provider: attachment.llmProvider, model: attachment.llmModel, promptUsed: attachment.llmPromptUsed, rawResponse: attachment.llmRawResponse, jsonSchema: attachment.llmJsonSchema, imageSource: attachment.llmImageSource, attempts: attachment.llmAttempts, usage: { promptTokens: attachment.llmPromptTokens, completionTokens: attachment.llmCompletionTokens, totalTokens: attachment.llmTotalTokens, }, } } return NextResponse.json(response) } catch (error) { console.error('Error getting parse status:', error) return NextResponse.json({ error: 'Failed to get parsing status' }, { status: 500 }) } }) /** * DELETE - Cancel/reset parsing status * * Allows user to cancel a stuck or in-progress parsing operation. * Resets the parsing status to null so they can retry. */ export const DELETE = withAuth(async (_request, { params }) => { try { const { playerId, attachmentId } = (await params) as { playerId: string; attachmentId: string } if (!playerId || !attachmentId) { return NextResponse.json({ error: 'Player ID and Attachment ID required' }, { status: 400 }) } // Authorization check const userId = await getUserId() const canModify = await canPerformAction(userId, playerId, 'start-session') if (!canModify) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } // Get attachment to verify it exists and belongs to player const attachment = await db .select() .from(practiceAttachments) .where(eq(practiceAttachments.id, attachmentId)) .get() if (!attachment || attachment.playerId !== playerId) { return NextResponse.json({ error: 'Attachment not found' }, { status: 404 }) } // Reset parsing status await db .update(practiceAttachments) .set({ parsingStatus: null, parsedAt: null, parsingError: null, rawParsingResult: null, approvedResult: null, confidenceScore: null, needsReview: null, // Clear LLM metadata llmProvider: null, llmModel: null, llmPromptUsed: null, llmRawResponse: null, llmJsonSchema: null, llmImageSource: null, llmAttempts: null, llmPromptTokens: null, llmCompletionTokens: null, llmTotalTokens: null, }) .where(eq(practiceAttachments.id, attachmentId)) return NextResponse.json({ success: true, message: 'Parsing cancelled', }) } catch (error) { console.error('Error cancelling parse:', error) return NextResponse.json({ error: 'Failed to cancel parsing' }, { status: 500 }) } }) |